package org.bookie.framework.services.impl;

import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.tapestry.services.CookieSource;
import org.bookie.core.services.IAuthenticationContext;
import org.bookie.model.Role;
import org.bookie.model.User;
import org.bookie.model.services.UserDAO;
import org.bookie.stateful.StatefulServiceLifecycleListener;
import org.hibernate.ObjectNotFoundException;

/**
 * Default implementation of the {@link IAuthenticationContext}
 * 
 * @author zubairov
 */
public class AuthenticationContextImpl implements IAuthenticationContext,
		Serializable, StatefulServiceLifecycleListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4001528290618866984L;

	/**
	 * User
	 */
	transient private User user;

	private Long userId;

	private CookieSource cookieSource;

	private UserDAO userDAO;

	private Log log;

	public static final String USER_ID_COOKIE_NAME = "org.bookie.userid";

	public static final String USER_HASH_COOKIE_NAME = "org.bookie.userhash";

	/**
	 * {@inheritDoc}
	 */
	public User getUser() {
		if (user == null) {
			log.debug("Trying to get authentication details from cookie");
			String userIdstr = cookieSource
					.readCookieValue(USER_ID_COOKIE_NAME);
			String userHash = cookieSource
					.readCookieValue(USER_HASH_COOKIE_NAME);
			if (userIdstr != null && userHash != null) {
				try {
					User probableUser = userDAO.findById(new Long(Long
							.parseLong(userIdstr)));
					if (probableUser != null) {
						if (probableUser.getRole() == Role.GUEST) {
							log.debug("User with ID=" + userIdstr
									+ " shouldn't be considered for autologin"
									+ " since he/she is a GUEST");
							// In case user from cookies was deleted
							cookieSource.removeCookieValue(USER_ID_COOKIE_NAME);
							cookieSource
									.removeCookieValue(USER_HASH_COOKIE_NAME);
						} else if (probableUser.calculateHash()
								.equals(userHash)) {
							log.info("User " + probableUser.getEmail()
									+ " was authenticated from cookies");
							user = probableUser;
						}
					}
				} catch (NumberFormatException e) {
					log.debug("Can't convert " + userIdstr + " to Long", e);
				} catch (ObjectNotFoundException e) {
					log.debug("Can't find user with ID=" + userIdstr
							+ " removing cookies");
					// In case user from cookies was deleted
					cookieSource.removeCookieValue(USER_ID_COOKIE_NAME);
					cookieSource.removeCookieValue(USER_HASH_COOKIE_NAME);
				}
			} else {
				log.debug("Can't read user cookies");
			}
		}
		return user;
	}

	/**
	 * {@inheritDoc}
	 */
	public AuthenticationResult authenticate(String email, String password,
			boolean rememberMe) {
		User user = userDAO.findByEmail(email);
		if (user == null)
			return AuthenticationResult.USER_NOT_FOUND;
		if (Role.GUEST.equals(user.getRole()))
			return AuthenticationResult.USER_NOT_ACTIVE;
		if (!user.hasPassword(password))
			return AuthenticationResult.EMAIL_PASSWORD_DISCREPANCE;
		if (rememberMe) {
			cookieSource.writeCookieValue(USER_ID_COOKIE_NAME, String
					.valueOf(user.getId()));
			cookieSource.writeCookieValue(USER_HASH_COOKIE_NAME, user
					.calculateHash());
		}
		this.user = user;
		return AuthenticationResult.SUCCESS;
	}

	/**
	 * {@inheritDoc}
	 */
	public AuthenticationResult authenticate(String email) {
		User user = userDAO.findByEmail(email);
		if (user == null) {
			return AuthenticationResult.USER_NOT_FOUND;
		}
		this.user = user;
		return AuthenticationResult.SUCCESS;
	}

	/**
	 * {@inheritDoc}
	 */
	public void setUser(User user) {
		this.user = user;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isUserInRole(Role role) {
		if (!role.equals(Role.GUEST) && !isUserAuthenticated()) {
			return false;
		}
		if (role.equals(Role.USER)) {
			if (Role.GUEST.equals(getUser().getRole())) {
				return false;
			}
		}
		if (role.equals(Role.ADMIN) && !Role.ADMIN.equals(getUser().getRole())) {
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean isUserAuthenticated() {
		return getUser() != null;
	}

	/**
	 * Setter for {@link CookieSource} service
	 * 
	 * @category injector
	 * @param cookieSource
	 */
	public void setCookieSource(CookieSource cookieSource) {
		this.cookieSource = cookieSource;
	}

	/**
	 * Setter for the log service
	 * 
	 * @category injector
	 * @param log
	 */
	public void setLog(Log log) {
		this.log = log;
	}

	/**
	 * Setter for injector
	 * 
	 * @category injector
	 * @param userDAO
	 */
	public void setUserDAO(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	/**
	 * {@inheritDoc}
	 */
	public void logout() {
		log.info("Logout from user " + getUser());
		cookieSource.removeCookieValue(USER_ID_COOKIE_NAME);
		cookieSource.removeCookieValue(USER_HASH_COOKIE_NAME);
	}

	/**
	 * {@inheritDoc}
	 */
	public void pauseConversation() {
		if (user != null) {
			log.debug("Pause conversation for user: " + user);
			userId = user.getId();
		}
		user = null;
	}

	/**
	 * {@inheritDoc}
	 */
	public void resumeConversation() {
		if (userId != null) {
			user = userDAO.findById(userId);
			log.debug("Resume conversation for user: " + user);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void terminateConversation() {
		userId = null;
	}

}
